﻿<!--#include file="zh.asp" -->
<%
'缓存管理模块
'
'主要功能：把最近访问的数据保存在application对象中，下次访问的时候直接从内存读取
'对缓存在内存中的类设有最大缓存实例数，最近访问的对象置于队列的最右段。
'如果达到缓存数目则替换出最近没有访问的对象
'
'author: evan zhao
'
'
' function getFromCache(name, id)
' sub putIntoCache(name, id, obj)
' sub putIntoCacheWithTime(name, id, obj, cachedTime)
' function removeFromCache(name, id)
' function removeAllFromCache()
'


 const USE_CACHE = true
 
 const DEFAULT_CACHE_SIZE=200 '默认最大缓存数量
 const DEFAULT_CACHE_TIMEOUT=20 '默认缓存过期时间（分钟）
 
 dim GLOBAL_CACHE_PREFIX 

 GLOBAL_CACHE_PREFIX = "CACHE_MISSLOG_" & MISSLOG_HOST_ID & "_"
 
 
 function getMaxCacheSize(name)
    dim maxCacheSize
    maxCacheSize = eval(name & "_CACHE_SIZE")
    if isEmpty(maxCacheSize) then
        maxCacheSize = DEFAULT_CACHE_SIZE
    end if
    getMaxCacheSize = clng(maxCacheSize)
 end function
 
 
 REM 取超时时间
 function getCacheTimeout(name)
    dim cacheTimeout
    cacheTimeout = eval(name & "_CACHE_TIMEOUT")
    if isEmpty(cacheTimeout) then
        cacheTimeout = DEFAULT_CACHE_TIMEOUT
    end if 
    getCacheTimeout = clng(cacheTimeout*60)

 end function
 
 function getFromCache(name, id)
    if USE_CACHE<>true then
        exit function
    end if

    dim maxCacheSize
    maxCacheSize = getMaxCacheSize(name)

    dim prefix
    prefix = GLOBAL_CACHE_PREFIX & name

    dim cachedSize, cachedIDArray, pos
    cachedSize = application(prefix & ".cachedSize")
    if isEmpty(cachedSize) or cachedSize<1 then
        exit function
    end if
    cachedIDArray = application(prefix & ".cachedIDArray")
    pos = getPosInArray(cachedIDArray, id)
    if pos<0 then
       exit function
    end if
    
    dim cachedItem, obj, cachedTime
    cachedItem = application(prefix & ".cachedItems(" & id & ")")
    if not isArray(cachedItem) then
       call removeFromCache(name, id)
       exit function
    elseif ubound(cachedItem)<>4 then   
       call removeFromCache(name, id)
       exit function
    'elseif  typename(cachedItem(1))<>"Single" and typename(cachedItem(1))<>"Double" and typename(cachedItem(1))<>"Integer" then
    '   response.write name&id&"="&cachedItem(1)
    '   call removeFromCache(name, id)
    '   exit function
    end if
    cachedTime = cdbl(cachedItem(0))
    
    dim cacheTimeout
    cacheTimeout = getCacheTimeout(name)

    REM 如果缓存内容已过期则清除(每天0点清除所有缓存)
    if cachedTime>timer() or cachedTime+cacheTimeout<timer() then
       call removeFromCache(name, id)
       exit function
    end if

	REM 根据用户区域选项获取相应语言下的数据
	dim lang, converting
	converting = false
	lang = Session(GLOBAL_CACHE_PREFIX & "lang")
	if "zh_cn" = LCASE(lang) or "zh-cn" = LCASE(lang) then
		if isEmpty(cachedItem(2)) then
		    if not isEmpty(cachedItem(1)) then
			    cachedItem(2) = simplized(cachedItem(1))
			else    
			    cachedItem(2) = simplized(cachedItem(3))
			end if
			converting = true
		end if
		obj = cachedItem(2)
	elseif "zh_tw" = LCASE(lang) or  "zh-tw" = LCASE(lang) then
		if isEmpty(cachedItem(3)) then
		    if not isEmpty(cachedItem(1))then
    			cachedItem(3) = traditionalized(cachedItem(1))
			else    
			    cachedItem(3) = traditionalized(cachedItem(2))
			end if
			converting = true
		end if
		obj = cachedItem(3)
	else
		obj = cachedItem(1)
	end if
	
    REM 如果缓存内容快被替出则移动到最新位置
    if (cachedSize>=maxCacheSize and pos<(cachedSize\5)) or converting then 
        call putIntoCacheWithTime(name, id, cachedItem(1),cachedItem(2),cachedItem(3), cachedTime)
    end if
    
    getFromCache = obj
 end function
 
 
 function removeFromCache(name, id)
    if USE_CACHE<>true then
        exit function
    end if

    dim prefix
    prefix = GLOBAL_CACHE_PREFIX & name

    dim cachedSize, cachedIDArray, pos
    dim cachedItem

    Application.Lock
    cachedSize = application(prefix & ".cachedSize")
    if not isEmpty(cachedSize) and cachedSize>0 then
        cachedIDArray = application(prefix & ".cachedIDArray")
        pos = getPosInArray(cachedIDArray, id)
        if pos>=0 then
            cachedItem = application(prefix & ".cachedItems(" & id & ")")
            call popFromArray(cachedIDArray, pos)
            cachedSize = cachedSize-1
            application(prefix & ".cachedSize") = cachedSize
            application(prefix & ".cachedIDArray") = cachedIDArray
            application.Contents.remove(prefix & ".cachedItems(" & id & ")")
        end if
        
    end if    
    Application.Unlock
    
    if isArray(cachedItem) then
        if ubound(cachedItem)>1 then
            removeFromCache = cachedItem(0)
        end if
    end if
 end function
 
 function removeManyFromCache(name)
    removeManyFromCache = false
    if USE_CACHE<>true then
        exit function
    end if

    dim prefix
    prefix = GLOBAL_CACHE_PREFIX & name

    dim cachedSize, cachedIDArray, pos
    dim cachedItem

    Application.Lock
    application(prefix & ".cachedSize") = Empty
    application(prefix & ".cachedIDArray") = Empty
    Application.Unlock
 
 end function
 
 function removeAllFromCache()
    removeAllFromCache = false
    dim attrName
    
    Application.Lock

    for each attrName in Application.Contents 
        if left(attrName,len(GLOBAL_CACHE_PREFIX))=GLOBAL_CACHE_PREFIX then
            application.Contents.remove(attrName)
        end if
    next

    Application.Unlock
    
    removeAllFromCache = true
 end function
 
 function putIntoCache(name, id, obj)
    dim obj_default, obj_zh_CN, obj_zh_TW
 
 	REM 根据用户区域选项获取相应语言下的数据
	dim lang, converting
	converting = false
	lang = Session(GLOBAL_CACHE_PREFIX & "lang")
	if "zh_cn" = LCASE(lang) then
		obj_zh_CN = simplized(obj)
		'obj_zh_TW = traditionalized(obj)
		putIntoCache = obj_zh_CN
	elseif "zh_tw" = LCASE(lang) then
	    obj_zh_TW = traditionalized(obj)
	    'obj_zh_CN = simplized(obj)
		putIntoCache = obj_zh_TW
	else
		obj_default = obj
	    'obj_zh_CN = simplized(obj)
		'obj_zh_TW = traditionalized(obj)
		putIntoCache = obj_default
	end if
	
    call putIntoCacheWithTime(name, id, obj_default,obj_zh_CN,obj_zh_TW, timer())
 end function

 sub putIntoCacheWithTime(name, id, obj,obj_zh_CN, obj_zh_TW, cachedTime)
    if USE_CACHE<>true then
        exit sub
    end if
    cachedTime = cdbl(cachedTime)
    
    dim maxCacheSize
    maxCacheSize = getMaxCacheSize(name)

    dim prefix
    prefix = GLOBAL_CACHE_PREFIX & name

    dim cachedSize, cachedIDArray, pos
    dim popIDs, popSize, i

    Application.Lock
    
    cachedSize = application(prefix & ".cachedSize")
    cachedIDArray = application(prefix & ".cachedIDArray")
    if isEmpty(cachedSize) or not isArray(cachedIDArray) then
        cachedSize = 0
        redim cachedIDArray(maxCacheSize)
    end if
    if cachedSize>maxCacheSize then
    	  cachedSize = maxCacheSize
        redim Preserve cachedIDArray(maxCacheSize)
    end if
    if ubound(cachedIDArray)<maxCacheSize then
        redim Preserve cachedIDArray(maxCacheSize)
    end if
    
		cachedSize = pushToArray(cachedIDArray, cachedSize, id)

    application(prefix & ".cachedSize") = cachedSize
    application(prefix & ".cachedIDArray") = cachedIDArray
    
    dim cacheItem(4)
    cacheItem(0) = cachedTime
    cacheItem(1) = obj
    cacheItem(2) = obj_zh_CN
    cacheItem(3) = obj_zh_TW
    application(prefix & ".cachedItems(" & id & ")")  = cacheItem
    if isArray(popIDs) then
        for i=0 to popSize-1 
            application.Contents.remove(prefix & ".cachedItems(" & popIDs(i) & ")")  
        next
    end if  
      
    Application.Unlock
 
 end sub

 function getPosInArray(array, id)
    if isEmpty(array) or not isArray(array) then
        exit function
    end if

    dim arraySize
    arraySize = UBOUND(array)
    if arraySize<1 then
        getPosInArray = -1
        exit function
    end if
    
    dim i
    for i=0 to arraySize-1 
        'response.write "\"&array(i)
        if array(i) = id then
            exit for
        end if
    next
    
    if array(i) <> id then
        i = -1
    end if
    getPosInArray = i
    
    'response.write("<div>pos=") & i &"</div>"
 end function
 
 function pushToArray(ByRef array, cachedSize, id)
    if isEmpty(array) or not isArray(array) then
        exit function
    end if

    dim arraySize, pos
    arraySize = UBOUND(array)
    
    if cachedSize<=0 then
        cachedSize = 0
        pos = -1
    else
        pos = getPosInArray(array, id)
    end if    
    
    if pos<0 and cachedSize = arraySize then  '不在缓存中并且缓存已满，替换出最早的对象
        popSize = cachedSize\5
        if popSize<1 then
            popSize=1
        end if    
        redim popIDs(popSize)
        for i=0 to popSize-1 
            popIDs(i) = popFromArray(cachedIDArray, 0)
        next
        cachedSize = cachedSize - popSize + 1
    elseif pos<0 then '不在缓存中并且缓存未满
        cachedSize = cachedSize + 1
    elseif pos<cachedSize-1 then ' 在缓存中，提为最新
        call popFromArray(array, pos)
    end if       
    
    array(cachedSize-1) = id
    
    pushToArray = cachedSize
 end function
 
 function popFromArray(array, pos)
    if isEmpty(array) or not isArray(array) then
        exit function
    end if

    dim arraySize
    arraySize = UBOUND(array)
    if arraySize<1 or pos<0 or pos>=arraySize then
        popFromArray = Empty
        exit function
    end if
    
    dim id, i
    id = array(pos)
    'response.write "<br>pop:" & pos &"="&id
    if pos<arraySize-1 then
        for i=pos to arraySize-2 
            array(i) = array(i+1)
        next
    end if
    array(arraySize-1) = empty
    
    popFromArray = id
 end function

 function array2String(array)
    dim j, str
    if isArray(array) then
        str = "{"
        for j=0 to ubound(array)-1
            if j>0 then
              str = str & ","
            end if
            str = str & array2String(array(j)) 
        next
        str = str & "}"
    else
        str = array
    end if
    array2String=str
 end function
 


 
class Person
   public name,gender,hair
   
    private sub class_initialize()

    end sub
    
    public function load()
       load = false
       dim array
       array = getFromCache("Person", name)
       if not isEmpty(array) and isArray(array) then
         if ubound(array)=3 then
           name = array(0)
           gender = array(1)
           hair = array(2)
           load = true
         end if  
       end if
   end function
   
   public sub cache()
       dim array(3)
       array(0) = name
       array(1) = gender
       array(2) = hair
       call putIntoCache("Person", name, array)
       'response.write("<p>save in cache</p>")
   end sub
   
   public sub clean()
       call removeFromCache("Person", name)
   end sub    
end class




function test
    response.CacheControl = "no-cache"
  
    ' dim tmp, index
    ' index =2
    ' tmp = getFromCache("BLOG",index)
    ' if isEmpty(tmp) then
    '    response.write "<br>not in cahce "
    '    tmp = index*10
    '    call putIntoCache("BLOG",index,tmp)
    ' else    
    '    response.write "<br>in cache "
    ' end if
    ' response.write "<br>"&index&"="&tmp
    ' call removeFromCache("BLOG",index)

    dim i, array(50)
    dim startTime, endTime
    array(0) = STRING(10000,"!")
    for i=1 to ubound(array)
        array(i) = STRING(20,"!")
    next
    
    'dim c
    'set c = new Person
    'c.name="evan"
    ''if not c.load() then
    '    c.gender="M"
    '    c.hair = array
    '    c.hair=array
    ''end if    
    'startTime = timer()
    'for i=0 to 100 
    '    call c.clean()
    '    call c.cache()
    '    call c.load()
    'next
    'endTime = timer()
    'response.write "cost " & (endTime-startTime)

    startTime = timer()
    for i=0 to 100 
        call removeFromCache("TestCache", "array")
        call putIntoCache("TestCache", "array", array)
        call getFromCache("TestCache", "array")
    next
    endTime = timer()

    response.write "cost " & (endTime-startTime)
end function    

'call test()

 %>